.text
.arm

.equ RIGHT,	0
.equ DOWN,	1
.equ LEFT,	2
.equ UP,	3

	bl Clear_Lcd
	bl Init_Output_Edge
	bl Init_Output_Snake
forever:
	bl Delay
	bl Update_Snake_Pos
	b forever

;=======================================
;		Delay a few time, waiting for 
;	user to press the key.
;=======================================
Delay:
	mov r3, #0x00007700
delay_loop:
	stmfd sp!, {lr}
	bl Update_dir
	ldmfd sp!, {lr}
	subs r3, r3, #1
	bne delay_loop
mov pc, lr

;=======================================
;		Update snake dir by the key
;	pressed.
;=======================================
Update_dir:
	stmfd sp!, {r0-r1}		;push to stack
	ldr r1, =snake_dir
	
	;Check what key was pressed, act accordingly.
	mov r0, #0
	swi 0x203

	cmp r0, #2
	moveq r0, #UP
	beq update_dir_normal

	cmp r0, #64
	moveq r0, #RIGHT
	beq update_dir_normal

	cmp r0, #32
	moveq r0, #DOWN
	beq update_dir_normal

	cmp r0, #16
	moveq r0, #LEFT
	beq update_dir_normal

	;None of the keys were pressed. Do nothing.
	ldmfd sp!, {r0-r1}		;pop from stack
	mov pc, lr				;return

update_dir_normal:
	str r0, [r1]
	ldmfd sp!, {r0-r1}		;pop from stack
mov pc, lr					;return

;=======================================
;		Outut edge
;=======================================
Init_Output_Edge:
	stmfd sp!, {r0-r3}		;push to stack

	mov r0, #0				;left
	mov r1, #0				;up
	mov r2, #'-'
	mov r3, #39				;loop times

upper_row_loop:
	swi 0x207
	add r0, r0, #1

	subs r3, r3, #1
	bne upper_row_loop
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	mov r0, #39				;left
	mov r1, #0				;up
	mov r2, #'|'
	mov r3, #15
righter_col_loop:
	swi 0x207
	add r1, r1, #1

	subs r3, r3, #1
	bne righter_col_loop
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	mov r0, #39				;left
	mov r1, #14				;up
	mov r2, #'-'
	mov r3, #39				;loop times

lower_row_loop:
	swi 0x207
	sub r0, r0, #1

	subs r3, r3, #1
	bne lower_row_loop
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	mov r0, #0				;left
	mov r1, #14				;up
	mov r2, #'|'
	mov r3, #15
lefter_col_loop:
	swi 0x207
	sub r1, r1, #1

	subs r3, r3, #1
	bne lefter_col_loop
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	ldmfd sp!, {r0-r3}		;pop from stack
mov pc, lr

;=======================================
;		Output snake to lcd
;=======================================
Init_Output_Snake:
	stmfd sp!, {r0-r3, r5-r6}	;push to stack

	ldr r0, =snake_len
	ldr r3, [r0]				;snake len
	sub r3, r3, #1

	ldr r5, =snake_left
	ldr r6, =snake_up

output_snake_loop:
	ldr r0, [r5], #4
	ldr r1, [r6], #4
	mov r2, #'*'
	swi 0x207

	subs r3, r3, #1
	bne output_snake_loop

	ldr r0, [r5], #4
	ldr r1, [r6], #4
	mov r2, #'&'
	swi 0x207

	ldmfd sp!, {r0-r3, r5-r6}	;pop from stack
mov pc, lr

;=======================================
;		Clear lcd
;=======================================
Clear_Lcd:
	stmfd sp!, {r0-r3}				;push to stack
	swi 0x206						;clear screen
	ldmfd sp!, {r0-r3}				;pop from stack
mov pc, lr

;=======================================
;		Updata snake positions
;=======================================
Update_Snake_Pos:
	stmfd sp!, {r0-r3, r5-r6}		;push to stack

	;;;;;;;;;;;;;;  preparations begin ;;;;;;;;;;;;;;
	ldr r0, =snake_len
	ldr r3, [r0]					;snake len

	ldr r5, =snake_left				;&snake_left
	ldr r6, =snake_up				;&snake_up
	;;;;;;;;;;;;;;  preparations end  ;;;;;;;;;;;;;;;

	stmfd sp!, {lr}
	bl Get_next_head_pos			;<r9>->left, <r10>->up
	ldmfd sp!, {lr}

	stmfd sp!, {lr}
	bl Check_wall_ok
	ldmfd sp!, {lr}
	cmp r0, #1
	beq edge_ok

	;The snake will reach the wall
	swi 0x206						;clear screen
	mov r0, #10						;left : 10
	mov r1, #5						;up : 5
	ldr r2, =game_over
	swi 0x204						;output to lcd
	swi 0x11						;exit this program

edge_ok:
	ldr r0, =count
	ldr r1, [r0]
	add r1, r1, #1
	str r1, [r0]
	cmp r1, #7
	beq longer

	ldr r0, =snake_left				;clear tail
	ldr r0, [r0]
	ldr r1, =snake_up
	ldr r1, [r1]
	mov r2, #' '
	swi 0x207

	mov r0, r7						;make the old head to body
	mov r1, r8
	mov r2, #'*'
	swi 0x207

	mov r0, r9						;print the new head
	mov r1, r10
	mov r2, #'&'
	swi 0x207

	stmfd sp!, {lr}					;update snake's pos in memory
	bl Copy_old_pos_Set_new_head
	ldmfd sp!, {lr}

	ldmfd sp!, {r0-r3, r5-r6}		;pop from stack
	mov pc, lr						;return

longer:
	ldr r0, =count
	mov r1, #0
	str r1, [r0]

	mov r0, r7						;make the old head to body
	mov r1, r8
	mov r2, #'*'
	swi 0x207

	mov r0, r9						;print the new head
	mov r1, r10
	mov r2, #'&'
	swi 0x207
	
	stmfd sp!, {lr}					;update snake's pos in memory
	bl Snake_become_logner
	ldmfd sp!, {lr}

	ldmfd sp!, {r0-r3, r5-r6}		;pop from stack
mov pc, lr							;return

;=======================================
;		Get pos of next head if snake
;	moves on.
;	input:
;		<r3>: snake len
;		<r5>: &snake_left
;		<r6>: &snake_up
;	return:
;		<r7>: current left
;		<r8>: current up
;		<r9>: next left
;		<r10>: next up
;=======================================
Get_next_head_pos:
	stmfd sp!, {r0-r3, r5-r6}		;push to stack

	sub r3, r3, #1
	mov r3, r3, lsl #2				;r3 *= 4
	add r5, r5, r3
	add r6, r6, r3

	ldr r9, [r5]					;current head left
	mov r7, r9
	ldr r10, [r6]					;current head up
	mov r8, r10

	ldr r0, =snake_dir
	ldr r0, [r0]
	mov r0, r0, lsl #3				;r0 = 8 * snake_dir

	ldr r3, =dirs
	ldr r1, [r3, r0]				;x bias
	add r0, r0, #4
	ldr r2, [r3, r0]				;y bias

	add r9, r9, r1					;next head left
	add r10, r10, r2					;next head up

	ldmfd sp!, {r0-r3, r5-r6}		;pop from stack
mov pc, lr

;=======================================
;		When next head pos is not food,
;	move old snake seg to new place,
;	and update head to new place.	
;	Input:
;		<r3>: snake len
;		<r5>: &snake_left
;		<r6>: &snake_up
;		<r9>: next head left
;		<r10>: next head up
;	Output:
;		None
;=======================================
Copy_old_pos_Set_new_head:
	stmfd sp!, {r0, r3, r5-r6}		;push to stack

	sub r3, r3, #1
copy_old_pos_loop:
	ldr r0, [r5, #4]
	str r0, [r5], #4

	ldr r0, [r6, #4]
	str r0, [r6], #4

	subs r3, r3, #1
	bne copy_old_pos_loop

	str r9, [r5]
	str r10, [r6]

	ldmfd sp!, {r0, r3, r5-r6}		;pop from stack
mov pc, lr

;=======================================
;		Snake become longer. Here we 
;	need to increase the variable <snake_len>,
;	and write the new head to snake pos
;	vector.
;	Input:
;		<r3>: snake len
;		<r5>: &snake_left
;		<r6>: &snake_up
;		<r9>: next head left
;		<r10>: next head up
;	Output:
;		None
;=======================================
Snake_become_logner:
	stmfd sp!, {r0, r1, r3}			;push to stack
	ldr r0, =snake_len
	ldr r1, [r0]
	add r1, r1, #1					;increase the length
	str r1, [r0]

	mov r3, r3, lsl #2				;r3 *= 4
	str r9, [r5, r3]				;write the new head
	str r10, [r6, r3]
	ldmfd sp!, {r0, r1, r3}			;pop from stack
mov pc, lr

;=======================================
;		Check edge. Find out whether
;	next pos is Wall. If true, let <r0>
;	be 0, else 1.
;		The area whether the snake moves
;	is a square width 38, height 13, the
;	postion starts from (1, 1) ends at 
;	(38, 13).
;	Input:
;		<r9>: next left
;		<r10>: next up
;	Output:
;		<r0>: 1 if legal else 0.
;=======================================
Check_wall_ok:
	cmp r9, #38
	bhi wall_not_ok
	cmp r10, #13
	bhi wall_not_ok
	cmp r9, #0
	beq wall_not_ok
	cmp r10, #0
	beq wall_not_ok

	mov r0, #1
	mov pc, lr					;return 1

wall_not_ok:
	mov r0, #0
mov pc, lr						;return 0

.data
	;<a href = "https://www.cnblogs.com/tureno/articles/12156395.html">
	;    ARM汇编数据定义伪指令参考链接
	;</a>
	game_over:	.asciz "Game Over"

	count:		.word 0
	snake_len:	.word 3
	snake_dir:	.word RIGHT
	dirs:		.word 1, 0, 0, 1, -1, 0, 0, -1

	;记录了蛇身体每一节的坐标
	;蛇的最大长度为15
	snake_left:	.word 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
	snake_up:	.word 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.end
